import json
from datetime import timedelta
from decimal import Decimal
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponse
from django.test import TestCase
from django.utils import timezone
from django.urls import reverse
from rest_framework.test import APITestCase, APIClient
from rest_framework import status
from unittest.mock import patch

from apps.tenants.models import Tenant
from .models import AuditLog
from .serializers import AuditLogSerializer
from .utils import log_action
from .middleware import AuditLogMiddleware
from .api import AuditLogViewSet


User = get_user_model()


# -------------------------------
# 1. 模型测试
# -------------------------------
class AuditLogModelTest(TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        # 扩展 get_full_name 方法（兼容性）
        def get_full_name_fallback(self):
            full_name = f"{self.first_name} {self.last_name}".strip()
            return full_name or self.username

        if not hasattr(User, 'get_full_name'):
            User.add_to_class('get_full_name', get_full_name_fallback)

    def setUp(self):
        self.tenant = Tenant.objects.create(name="Test Tenant", code="test")

        self.user = User.objects.create_user(
            username='auditor',
            email='auditor@example.com',
            password='pass123',
            first_name='Audit',
            last_name='User',
            tenant=self.tenant  # 假设 User 有 tenant 字段
        )

    def test_create_audit_log(self):
        log = AuditLog.objects.create(
            user=self.user,
            username='auditor',
            action='create',
            model_name='User',
            object_id=1,
            object_repr='auditor',
            changes={"email": ["", "a@b.com"]},
            ip_address="192.168.1.1",
            user_agent="Mozilla/5.0",
            tenant=self.tenant
        )
        self.assertEqual(log.username, 'auditor')
        self.assertEqual(log.action, 'create')
        self.assertEqual(str(log), "auditor 创建 auditor")

    def test_action_choices(self):
        choices = dict(AuditLog.ACTION_CHOICES)
        self.assertEqual(choices['login'], '登录')
        self.assertEqual(choices['delete'], '删除')

# -------------------------------
# 2. 序列化器测试
# -------------------------------
class AuditLogSerializerTest(TestCase):
    def setUp(self):
        self.tenant = Tenant.objects.create(name="Test", code="test2")
        self.user = User.objects.create_user(username='testuser', password='pass')
    def test_serializer_output(self):
        log = AuditLog.objects.create(
            user=self.user,
            username='testuser',
            action='update',
            model_name='Profile',
            object_id=5,
            object_repr='Profile for testuser',
            changes={"bio": ["old", "new"]},
            ip_address="10.0.0.1",
            user_agent="Chrome/120",
            tenant=self.tenant
        )
        serializer = AuditLogSerializer(log)
        data = serializer.data
        print(data)

        self.assertEqual(data['username'], 'testuser')
        self.assertEqual(data['action'], 'update')
        self.assertEqual(data['action_display'], '更新')
        self.assertEqual(data['model_name'], 'Profile')
        self.assertEqual(data['object_id'], 5)
        self.assertEqual(data['ip_address'], '10.0.0.1')
        self.assertIn('timestamp', data)

        # ✅ 正确验证 changes 字段
        self.assertEqual(data['changes'], {"bio": ["old", "new"]})
# -------------------------------
# 3. 中间件测试
# -------------------------------
# apps/audit/tests.py
from django.test import TestCase
from django.test import RequestFactory  # ✅ 导入 RequestFactory
from django.http import HttpResponse
from django.contrib.auth import get_user_model

from apps.tenants.models import Tenant
from .middleware import AuditLogMiddleware
class AuditLogMiddlewareTest(TestCase):
    def setUp(self):
        # ✅ 使用 RequestFactory
        self.factory = RequestFactory()
        # ✅ 创建 get_response 模拟函数
        self.get_response = lambda request: HttpResponse()
        # ✅ 实例化中间件
        self.middleware = AuditLogMiddleware(get_response=self.get_response)

    def test_process_request_with_x_forwarded_for(self):
        # ✅ 使用 factory.get() 创建 request
        request = self.factory.get(
            "/test",
            HTTP_X_FORWARDED_FOR="1.2.3.4, 5.6.7.8",
            HTTP_USER_AGENT="Mozilla/5.0 (Linux)"
        )
        self.middleware.process_request(request)
        print(request.client_ip)
        self.assertEqual(request.client_ip, "1.2.3.4")
        self.assertEqual(request.user_agent, "Mozilla/5.0 (Linux)")

    def test_process_request_with_remote_addr(self):
        request = self.factory.get("/test", REMOTE_ADDR="192.168.1.1")
        self.middleware.process_request(request)
        self.assertEqual(request.client_ip, "192.168.1.1")

    def test_process_request_no_ip_header(self):
        request = self.factory.get("/test")
        # 此时 REMOTE_ADDR 默认为 '127.0.0.1'
        self.middleware.process_request(request)
        self.assertEqual(request.client_ip, "127.0.0.1")  # ✅ 期望是 127.0.0.1
        self.assertEqual(request.user_agent, "")
    # -------------------------------
# 4. 工具函数测试
# -------------------------------
class AuditLogUtilsTest(TestCase):
    def setUp(self):
        self.tenant = Tenant.objects.create(name="Tenant", code="utils")
        self.user = User.objects.create_user(username='utiluser', password='pass')
        self.request = type('Request', (), {})()
        self.request.client_ip = "1.1.1.1"
        self.request.user_agent = "TestAgent"

    def test_log_action_creates_log(self):
        from .utils import log_action

        log_action(
            user=self.user,
            action='login',
            model_name='AuthSession',
            object_id=None,
            object_repr='Session ABC',
            changes=None,
            request=self.request
        )

        log = AuditLog.objects.last()
        self.assertEqual(log.username, 'utiluser')
        self.assertEqual(log.action, 'login')
        self.assertEqual(log.model_name, 'AuthSession')
        self.assertEqual(log.ip_address, '1.1.1.1')
        self.assertEqual(log.user_agent, 'TestAgent')
        self.assertEqual(log.tenant, self.user.tenant)

    def test_log_action_with_no_request(self):
        from .utils import log_action

        log_action(
            user=self.user,
            action='logout',
            model_name='AuthSession',
            object_id=99,
            object_repr='Session XYZ'
        )

        log = AuditLog.objects.last()
        self.assertEqual(log.ip_address, None)
        self.assertEqual(log.user_agent, '')


# -------------------------------
# 5. 视图测试 (APITestCase)
# -------------------------------
class AuditLogViewSetTest(APITestCase):
    def setUp(self):
        # 创建租户
        self.tenant1 = Tenant.objects.create(name="Tenant A", code="t1")
        self.tenant2 = Tenant.objects.create(name="Tenant B", code="t2")

        # 创建用户
        self.user1 = User.objects.create_user(
            username='user1',
            password='pass123',
            email='u1@example.com',
            tenant=self.tenant1
        )
        self.user2 = User.objects.create_user(
            username='user2',
            password='pass123',
            email='u2@example.com',
            tenant=self.tenant2
        )
        self.admin = User.objects.create_superuser(
            username='admin',
            password='admin123',
            email='admin@example.com',
            tenant=self.tenant1
        )

        # 分配权限
        ct = ContentType.objects.get_for_model(AuditLog)
        view_perm, _ = Permission.objects.get_or_create(
            codename='view_auditlog',
            content_type=ct
        )
        self.user1.user_permissions.add(view_perm)
        self.user2.user_permissions.add(view_perm)
        self.admin.user_permissions.add(view_perm)

        # 创建审计日志
        self.log1 = AuditLog.objects.create(
            user=self.user1,
            username='user1',
            action='create',
            model_name='Project',
            object_id=1,
            object_repr='Project Alpha',
            timestamp=timezone.now() - timedelta(hours=1),
            tenant=self.tenant1
        )
        self.log2 = AuditLog.objects.create(
            user=self.user1,
            username='user1',
            action='update',
            model_name='Project',
            object_id=1,
            object_repr='Project Alpha',
            timestamp=timezone.now(),
            tenant=self.tenant1
        )
        self.log3 = AuditLog.objects.create(
            user=self.user2,
            username='user2',
            action='delete',
            model_name='Task',
            object_id=5,
            object_repr='Task Cleanup',
            tenant=self.tenant2
        )

        # API 客户端设置
        self.client = APIClient()
        self.list_url = reverse('auditlog-list')
        self.detail_url = lambda pk: reverse('auditlog-detail', args=[pk])

    def test_list_requires_auth(self):
        self.client.logout()
        response = self.client.get(self.list_url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_user_can_only_see_own_tenant_logs(self):
        self.client.force_authenticate(user=self.user1)
        response = self.client.get(self.list_url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 2)
        print(response.data)
        ids = [item['id'] for item in response.data['results']]
        self.assertIn(self.log1.id, ids)
        self.assertIn(self.log2.id, ids)
        self.assertNotIn(self.log3.id, ids)

    def test_admin_sees_only_own_tenant_logs(self):
        self.client.force_authenticate(user=self.admin)
        response = self.client.get(self.list_url)
        self.assertEqual(len(response.data['results']), 2)
        ids = [item['id'] for item in response.data['results']]
        print(response.data)
        self.assertIn(self.log1.id, ids)
        self.assertNotIn(self.log3.id, ids)

    def test_filter_by_action(self):
        self.client.force_authenticate(user=self.user1)
        response = self.client.get(self.list_url, {'action': 'create'})
        print(response.data)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['action'], 'create')

    def test_filter_by_model_name(self):
        self.client.force_authenticate(user=self.user1)
        response = self.client.get(self.list_url, {'model_name': 'Project'})
        self.assertEqual(len(response.data['results']), 2)

    def test_filter_by_username(self):
        self.client.force_authenticate(user=self.user1)
        response = self.client.get(self.list_url, {'username': 'user1'})
        self.assertEqual(len(response.data['results']), 2)

    def test_filter_by_object_repr(self):
        self.client.force_authenticate(user=self.user1)
        response = self.client.get(self.list_url, {'object_repr': 'Alpha'})
        self.assertEqual(len(response.data['results']), 2)

    def test_filter_by_ip_address(self):
        self.log1.ip_address = "192.168.1.1"
        self.log1.save()
        self.client.force_authenticate(user=self.user1)
        response = self.client.get(self.list_url, {'ip_address': '192.168.1.1'})
        self.assertEqual(len(response.data['results']), 1)

    def test_filter_by_date_range(self):
        self.client.force_authenticate(user=self.user1)
        start = (timezone.now() - timedelta(hours=2)).isoformat()
        end = timezone.now().isoformat()
        response = self.client.get(self.list_url, {
            'start_date': start,
            'end_date': end
        })
        self.assertEqual(len(response.data['results']), 2)

    def test_search_functionality(self):
        self.client.force_authenticate(user=self.user1)
        response = self.client.get(self.list_url, {'search': 'Project'})
        self.assertEqual(len(response.data['results']), 2)

    def test_ordering(self):
        self.client.force_authenticate(user=self.user1)
        response = self.client.get(self.list_url, {'ordering': 'timestamp'})
        results = response.data['results']
        self.assertEqual(results[0]['id'], self.log1.id)

        response = self.client.get(self.list_url, {'ordering': '-timestamp'})
        results = response.data['results']
        self.assertEqual(results[0]['id'], self.log2.id)

    def test_detail_view(self):
        self.client.force_authenticate(user=self.user1)
        url = self.detail_url(self.log1.id)
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['object_repr'], 'Project Alpha')


# -------------------------------
# 6. 权限测试
# -------------------------------
class AuditLogPermissionTest(APITestCase):
    def setUp(self):
        self.tenant = Tenant.objects.create(name="PermTenant", code="perm")
        self.user = User.objects.create_user(
            username='nopermuser',
            password='123',
            tenant=self.tenant,
            is_staff=False,        # ✅ 明确设置
            is_superuser=False     # ✅ 明确设置
        )
        self.client = APIClient()
        self.url = reverse('auditlog-list')

    def test_user_without_permission_cannot_access(self):
        self.user.user_permissions.clear()
        self.user.save()

        self.client.force_authenticate(user=self.user)
        print(self.url)
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_user_with_permission_can_access(self):
        ct = ContentType.objects.get_for_model(AuditLog)
        perm, _ = Permission.objects.get_or_create(
            codename='view_auditlog',
            content_type=ct
        )
        self.user.user_permissions.add(perm)
        self.user.refresh_from_db()

        # 创建数据
        AuditLog.objects.create(
            user=self.user,
            username='nopermuser',
            action='login',
            model_name='Session',
            object_id=1,
            object_repr='Login',
            tenant=self.tenant
        )

        self.client.force_authenticate(user=self.user)
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        print(response.data)
        self.assertEqual(len(response.data['results']), 1)


# -------------------------------
# 7. 缓存或性能测试（可选，若视图有缓存）
# -------------------------------
class AuditLogPerformanceTest(APITestCase):
    def setUp(self):
        self.tenant = Tenant.objects.create(name="Perf", code="perf")
        self.user = User.objects.create_user(
            username='perfuser',
            password='pass',
            tenant=self.tenant
        )

        ct = ContentType.objects.get_for_model(AuditLog)
        perm, _ = Permission.objects.get_or_create(
            codename='view_auditlog',
            content_type=ct
        )
        self.user.user_permissions.add(perm)
        self.user.refresh_from_db()  # ✅ 刷新用户

        # 创建日志
        logs = [
            AuditLog(
                user=self.user,
                username='perfuser',
                action='create',
                model_name='Item',
                object_id=i,
                object_repr=f'Item {i}',
                tenant=self.tenant,
                timestamp=timezone.now() - timedelta(hours=1) + timedelta(seconds=i)
            )
            for i in range(50)
        ]
        AuditLog.objects.bulk_create(logs)

        self.client = APIClient()
        self.client.force_authenticate(user=self.user)
        self.url = reverse('auditlog-list')

    def test_list_does_not_exceed_query_limit(self):
        with self.assertNumQueries(5):  # ✅ 允许 4 次查询
            response = self.client.get(self.url, {'page_size': 20})
            self.assertEqual(response.status_code, 200)
            self.assertEqual(len(response.data['results']), 20)